package cn.piflow.rpc

import cn.piflow.util.Logging

/**
	* Created by bluejoe on 2017/10/31.
	*/
/**
	* load existing class as binary byte array
	*/
trait ClassLoaderService {
	def loadClassAsStream(name: String): Array[Byte];
}

/**
	* able to add remote class service entry
	*/
trait RemoteClassLoaderService {
	/**
		* @param classLoaderServiceUrl url string of remote class loader server
		*/
	def registerRemoteClassLoaderServer(classLoaderServiceUrl: String);
}

class RemoteClassLoaderServiceImpl extends RemoteClassLoaderService with Logging {
	val urls = collection.mutable.Map[String, ClassLoaderService]();

	def registerRemoteClassLoaderServer(classLoaderServiceUrl: String) = {
		urls.put(classLoaderServiceUrl, new HttpRpcClient(classLoaderServiceUrl).
			createProxy(classOf[ClassLoaderService].getName, classOf[ClassLoaderService]));
	}

	def getClassLoader(parentLoader: ClassLoader): ClassLoader = {
		new ClassLoader(parentLoader) {
			override def findClass(name: String): Class[_] = {
				try {
					//if ok, return the class
					super.findClass(name);
				}
				catch {
					case e: ClassNotFoundException => {
						if (likeReplClass(name)) {
							logger.debug(s"looking up unknown class: $name");
							findClassRemotely(name);
						}
						else {
							throw e;
						}
					}
				}
			}

			def likeReplClass(name: String) =
				name.startsWith("$line") || name.startsWith("$iw$");

			def findClassRemotely(name: String): Class[_] = {
				var bytes: Array[Byte] = null;
				for (service <- urls.values if bytes == null) {
					bytes = service.loadClassAsStream(name);
				}

				defineClass(name, bytes, 0, bytes.length);
			}
		}
	}
}